/* Write to your own most recently active 'mesg y' terminal.
 *
 * Copyright 2008 Cian Synnott.
 * 
 * $Id: write_self.c 19 2009-11-27 20:46:48Z cian $
 */
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <utmp.h>
#include <unistd.h>

#include "write_self.h"
#include "log.h"

/* Set the device prefix for ttys
 *
 * Defaults to /dev; this is exposed for testing.
 *
 * Args:
 * - prefix: string device prefix
 */
static char *device_prefix = "/dev";
/* See prototype comment in write_self.h */
void set_device_prefix(char *prefix) {
  device_prefix = prefix;
}

/* Finds your most recently active 'mesg y' terminal.
 *
 * The returned value points to static storage, so isn't thread-safe.
 *
 * Returns:
 * - device name, e.g. /dev/pts/1, or NULL on failure
 * Side effects:
 * - logs error conditions on failure
 */
static char *find_best_device(void) {
  static char best_device[MAXPATHLEN];
  char buffer[MAXPATHLEN];
  struct utmp *entry;
  struct passwd *pwd;
  struct stat device;
  time_t best_time = 0;

  errno = 0;
  pwd = getpwuid(getuid());
  if (pwd == NULL) {
    if (errno)
      log_err("getpwuid failed: %s", strerror(errno));
    else
      log_err("getpwuid for %d returned nothing", getuid());
    return NULL;
  }

  setutent();
  while ((entry = getutent())) {
    if (entry->ut_type == USER_PROCESS
        && strcmp(pwd->pw_name, entry->ut_user) == 0) {
      snprintf(buffer, sizeof(buffer), "%s/%s", device_prefix, entry->ut_line);
      if (stat(buffer, &device) == -1) {
        log_err("stat failed for %s: %s", buffer, strerror(errno));
        return NULL;
      }
      if (device.st_mode & S_IWGRP) {
        if (device.st_atime > best_time) {
          strncpy(best_device, buffer, MAXPATHLEN);
          best_time = device.st_atime;
        }
      }
    }
  }
 
  if (best_time == 0) {
    log_err("found no 'mesg y' terminals for %s", pwd->pw_name);
    return NULL;
  }

  return best_device;
}

/* See prototype comment in write_self.h */
int write_self(const char *text) {
  int fd;

  char *best_device = find_best_device();
  if (best_device == NULL) {
    log_err("best_device failed");
    return -1;
  }

  fd = open(best_device, O_WRONLY);

  if (fd == -1) {
    log_err("open of %s failed: %s", best_device, strerror(errno));
    return -1;
  }

  if (write(fd, text, strlen(text)) != strlen(text)) {
    log_warn("write failed: %s", strerror(errno));
  }

  if(close(fd) == -1) {
    log_err("close of %s failed: %s", best_device, strerror(errno));
    return -1;
  }
  return 0;
}
